<?php
declare(strict_types=1);

namespace app\traits;

use app\model\AdminMenu;
use app\model\AdminPermission;
use app\model\AdminUser;
use think\captcha\facade\Captcha;
use think\facade\Log;
use think\facade\Session;
use app\model\AdminRolePermission;
use Symfony\Component\Filesystem\Filesystem;

trait MyControllerTrait
{
    /**
     * 手机号/座机号验证
     *
     * @param string $mobile
     * @param int    $type
     * @return bool
     */
    public function numberVerification(string $mobile, int $type=3): bool
    {
        $pattern1 = "/(1[3-9])\d{9}$/";// 手机号
        $pattern2 = '/(\d{3,4}-)?[0-9]{7,8}/';// 座机号
        $b1 = preg_match($pattern1, $mobile);
        $b2 = preg_match($pattern2, $mobile);

        // dump($b1, $b2);
        switch($type){
            case 1:
                if ($b1) return true;
                break;
            case 2:
                if ($b2) return true;
                break;
            case 3:
                if ($b1) return true;
                if ($b2) return true;
                break;
        }
        return false;
    }// numberVerification() end

    /**
     * 下载文件
     *
     * @param string $filename  文件绝对路径
     * @return void
     */
    public function downloadFile(string $filename)
    {
        header("Content-type:".mime_content_type($filename));
        header('Content-Disposition: attachment; filename="' . basename($filename).'"');
        header("Pragma: no-cache");
        header("Expires: 0");
        readfile($filename);
    }// downloadFile() end

    /**
     * 文件格式判断
     *
     * @param string $file_format   audio/mpeg
     * @param string $format        audio
     * @return bool
     */
    public function isFileFormat(string $file_format, string $format): bool
    {
        $arr = explode('/', $file_format);
        if (empty($arr)) return false;
        return $format === array_shift($arr);
    }// isFileFormat() end

    /**
     * @param array $file   $file = $_FILES['file']
     * @return array
     */
    public function singleUpload(array $file): array
    {
        // {"name":"001.mp3","type":"audio/mpeg","tmp_name":"C:/Users/Administrator/AppData/Local/Temp/phpD526.tmp","error":0,"size":2994349}
        $name       = $file['name'];
        $type       = $file['type'];
        $tmp_name   = $file['tmp_name'];
        $error      = $file['error'];
        $size       = $file['size'];
        $fileSystem = new Filesystem;

        if ($error !== UPLOAD_ERR_OK)
        {
            if ($fileSystem->exists($tmp_name)) unlink($tmp_name);

            return ['code' => 0, 'message' => '上传失败'];
        }

        if (is_uploaded_file($tmp_name) === false)
        {
            if ($fileSystem->exists($tmp_name)) unlink($tmp_name);

            return ['code' => 0, 'message' => '上传失败'];
        }

        // 123.abc.345.png = > "png"
        $arr        = explode('.', $name);
        $suffix     = array_pop($arr);
        $new_name   = date('YmdHis') . mt_rand(1000, 9999) .'.'. $suffix;
        $tmp_path   = public_path('tmp').$new_name;

        $fileSystem->copy($tmp_name, $tmp_path);
        unlink($tmp_name);

        return [
            'code'      => 1,
            'message'   => '上传成功',
            'tmp_path'  => $tmp_path,
            'name'      => $new_name,
            'size'      => $size,
            'type'      => $type
        ];
    }// singleUpload() end

    /**
     * 获取访问权限标题
     *
     * @param string $uri
     * @return string
     */
    public function getPermissionTitle(string $uri = ""): string
    {
        return AdminPermission::where("uri = :uri", ['uri' => trim($uri, '/')])->value('title') ?? "";
    }// getPermissionTitle() end

    /**
     * 递归删除目录
     *
     * @param string $dir
     * @return void
     */
    public function deleteDir(string $dir)
    {
        $handle   = opendir($dir);
        $filename = readdir($handle);
        while(false !== $filename = readdir($handle)){
            //dump($filename);
            if ($filename == '.' || $filename == '..') continue;
            if(is_dir($dir.'/'.$filename))
            {
                $this->deleteDir($dir.'/'.$filename);
            } else {
                unlink($dir.'/'.$filename);
            }
        }

        closedir($handle);
        if (is_dir($dir)) rmdir($dir);
    }// deleteDir() end
    
    /**
     * 获取对称加密 key，建议长度 8~15
     *
     * @return string
     */
    public function getAppKey(): string
    {
        return config("app.app_key", '');
    }// getAppKey() end

    /**
     * 获取对称加密 secret，建议长度 20~32
     *
     * @return string
     */
    public function getAppSecret(): string
    {
        return config("app.app_secret", '');
    }// getAppSecret() end

    /**
     * 获取菜单及其子菜单
     *
     * @param string $uri  "admin/menu/index"
     * @return array
     */
    public function getMenuNav(string $uri = ""): array
    {
        $menus         = AdminMenu::getAllPid();
        $admin_role_id = (int)Session::get('admin_user.admin_role_id', 0);
        foreach ($menus as $index => $val){
            $child_menus = AdminMenu::field('title, uri')
                ->where('pid', $val['id'])
                ->where('is_show', 1)
                ->order('rank', 'DESC')
                ->select()
                ->toArray();

            // layui 导航增加 .layui-this 悬浮选中的样式效果
            $menus[$index]['focus'] = false;
            foreach ($child_menus as $child_menu){
                if ($child_menu['uri'] === trim($uri, '/')) $menus[$index]['focus'] = true;

                //dump($child_menu['uri']);
                if (AdminUser::isSuperAdmin(Session::get('admin_user.account')))
                {
                    $menus[$index]['child_menus'][] = $child_menu;
                    continue;
                }
                if (AdminRolePermission::isPass($admin_role_id, $child_menu['uri'])) $menus[$index]['child_menus'][] = $child_menu;
            }
        }
        //dump($menus);
        /*array:1 [
            0 => array:3 [
            "id" => 1
            "title" => "系统设置"
            "focus" => true
            "child_menu" => array:2 [
                0 => array:2 [
                    "title" => "菜单"
                    "uri" => "admin/menu/index"
                ]
                1 => array:2 [
                    "title" => "参数"
                    "uri" => "admin/config/index"
                ]
            ]
          ]
        ]*/
        return $menus;
    }// getMenuNav() end

    /**
     * 获取验证码
     *
     * @return \think\Response
     */
    public function generateCaptcha(): \think\Response
    {
        return Captcha::create();
    }// getCaptcha() end

    /**
     * 对称加密
     *
     * @param string $str
     * @return string
     */
    public function encrypt(string $str): string
    {
        $key    = $this->getAppKey() . $this->getAppSecret();
        $cipher = "AES-128-CBC";
        $iv_len = openssl_cipher_iv_length($cipher);
        $iv     = openssl_random_pseudo_bytes($iv_len);
        $raw    = openssl_encrypt($str, $cipher, $key, \OPENSSL_RAW_DATA, $iv);
        $hmac   = hash_hmac('sha256', $raw, $key, true);
        return base64_encode( $iv.$hmac.$raw );
    }// encrypt() end

    /**
     * 对称解密
     *
     * @param string $str
     * @return string
     */
    public function decrypt(string $str): string
    {
        $key    = $this->getAppKey() . $this->getAppSecret();
        $cipher  = "AES-128-CBC";
        $sha2len = 32;
        $c       = base64_decode($str);
        $iv_len  = openssl_cipher_iv_length($cipher);
        $iv      = substr($c, 0, $iv_len);
        $hmac    = substr($c, $iv_len, $sha2len);
        $raw     = substr($c, $iv_len+$sha2len);
        $str     = openssl_decrypt($raw, $cipher, $key, \OPENSSL_RAW_DATA, $iv);

        if ($str === false) return '';
        return $str;
    }// decrypt() end

    /**
     * 导出 Excel 文件
     *
     * @param string $filename  文件名
     * @param array  $title     标题
     * @param array  $data      数据，二维索引数组
     */
    public function exportExcel(string $filename, array $title, array $data = [])
    {
        /*$filename = "demo";
        $title    = ['标题-1', '标题-2'];
        $data     = [
            ['内容-1', '内容-1'],
            ['内容-2', '内容-2'],
        ];*/
        $suffix    = '.xls';
        $filename .= $suffix;
        $keys      = array_keys($title);
        $values    = array_values($title);

        header("Content-type:application/octet-stream");
        header("Accept-Ranges:bytes");
        //设置内容类型
        header("Content-type:application/vnd.ms-excel");
        //文件下载
        header("Content-Disposition:attachment;filename={$filename}");
        header("Pragma: no-cache");
        header("Expires: 0");

        $in_charset  = "UTF-8";
        $out_charset = "GBK";
        /*foreach ($title as $value){
            echo iconv($in_charset, $out_charset, $value) . "\t";
        }
        echo "\n";

        foreach($data as $index => $value){
            foreach ($value as $index2 => $value2){
                echo iconv($in_charset, $out_charset, $value2) . "\t";
            }
            echo "\n";
        }*/
        foreach ($values as $value){
            echo iconv($in_charset, $out_charset, $value) . "\t";
        }
        echo "\n";

        foreach($data as $dval){
            foreach ($keys as $kval){
                echo iconv($in_charset, $out_charset, (string)$dval[$kval]) . "\t";
            }
            echo "\n";
        }
    }// exportExcel() end

    /**
     * 生成随机字符串
     *
     * @param int    $length    长度
     * @param string $type      类型 string|int
     * @return string
     */
    public function generateString(int $length = 32, string $type = 'string'): string
    {
        $type   = strtolower($type);
        $string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        if ($type === "int") $string = "0123456789";

        $max = strlen($string) - 1;
        $str = '';
        for ($i=0; $i<$length; $i++) {
            $str .= $string[mt_rand(0, $max)];
        }
        return $str;
    }// generateString() end

    /**
     * 保留浮点数小数点位数，计算结果是四舍五舍
     * sprintf("%.2f", float $n) 计算结果是四舍五入
     *
     * @param float|string $num   浮点数
     * @param int          $len   保留小数点位数
     * @return float
     */
    public function keepDecimalPoint($num, int $len = 2): float
    {
        $arr   = explode('.', (string)$num);
        $point = 0;
        if (empty($arr[1]) === false) $point = substr($arr[1], 0, $len);
        $str   = $arr[0] .'.'. $point;
        return (float)$str;
    }// keepDecimalPoint() end
}